/*
 * RpcMethod.cpp
 *
 *   Created on: 2010-3-24     
 *  Description: 
 *    Copyright: 2010 @ ICT Li Heyuan
 */

#include "../../include/method/RpcMethod.h"
#include "../../include/util/JsonRpcException.h"
#include "../../include/crypto/RSATools.h"
#include "../../include/crypto/AESTools.h"
#include "../../include/util/Const.h"
#include "../../include/json_cpp/writer.h"
#include "../../include/json_cpp/reader.h"
#include "../../include/util/Util.h"

#include <iostream>
using std::cout;
using std::endl;

RpcMethod::RpcMethod()
{
	//默认不启用加密
	_isEncrypt = false;
}

RpcMethod::~RpcMethod()
{

}

bool RpcMethod::execute(Value &req, Value &resp, EasyAddr &addr, string& error)
{
	Json::FastWriter writer;
	try
	{
		AESTools aes;
		if (!autoDecrypt(req, addr.ip, aes))//解密失败
		{
			throw JsonRpcException("server RSA/AES decrypt fail.");
		}

		//param和ret
		Value param = req["params"]; //从request中抽取出参数

		string str = writer.write(req);

		//如果param非空，必须是数组
		if (param != Json::nullValue && !param.isArray())
		{
			throw JsonRpcException("params must be an array.");
		}

		//IP黑、白名单检查
		if (!checkIP(addr))
		{
			throw JsonRpcException("access is denied");
		}

		//执行用户逻辑
		Value result;//保存result结果
		bool ret = false;
		try
		{
			ret = execute_logic(param, result, addr, error);
		} catch (...)
		{
			throw JsonRpcException("Exception during execute_logic.");
		}

		resp[Const::ID] = req[Const::ID];

		if (!ret)
		{
			//执行失败
			resp["error"] = error;
			resp["result"] = Json::nullValue;
		}
		else
		{
			//执行成功
			resp["error"] = Json::nullValue;
			resp["result"] = result;
		}

		//自动加密result
		if (!autoEncrypt(resp, aes))//加密失败
		{
			throw JsonRpcException("server RSA/AES encrypt fail.");
		}

		return ret;
	} catch (JsonRpcException &err)
	{
		//出错
		error = err.what();
		//处理失败的情况
		resp["result"] = Value::null;
		resp["error"] = error;
		resp["id"] = req["id"];
		return false;
	} catch (...)
	{
		error = "JsonCpp parser fail";
		//是json_cpp出错
		resp["result"] = Value::null;
		resp["error"] = error;
		resp["id"] = req["id"];
		return false;
	}
}

bool RpcMethod::checkIP(const EasyAddr &addr)
{
	//黑名单优先级高
	if (!_blackList.empty())
	{
		set<string>::size_type cnt = _blackList.count(addr.ip);
		if (cnt >= 1)
		{
			return false;
		}
	}

	//如果有白名单，必须在白名单中才能通过
	if (!_whiteList.empty())
	{
		set<string>::size_type cnt = _whiteList.count(addr.ip);
		if (cnt >= 1)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	return true;
}

bool RpcMethod::autoDecrypt(Value &req, const string &ip, AESTools &aes)
{
	try
	{
		//没启用加密，直接成功
		if (!_isEncrypt)
		{
			return true;
		}

		//先检查是否有key和iv
		string key_cipher = req[Const::AES_KEY].asString();
		string iv_cipher = req[Const::AES_IV].asString();

		if (key_cipher.empty() || iv_cipher.empty())
		{
			//AES信息不完整，无需解密
			return false;
		}

		//用RSA_PRIVATE将key和iv解密
		RSATools* rsa = Util::getInstance()->getRSAToolsByIP(ip);

		if (rsa == NULL)//之前没有生成过RSATools
		{
			return false;
		}

		//RSA解密出AES的key和iv
		string key = rsa->decryptWithBase64(key_cipher);
		string iv = rsa->decryptWithBase64(iv_cipher);
		delete rsa;//注意释放RSATools


		Json::FastWriter writer;
		//使用对称密钥AES解密params，写入到params中
		string params_cipher = req[Const::PARAMS].asString();
		aes = AESTools(key, iv);
		string params_plain = aes.decryptWithBase64(params_cipher);
		Json::Reader reader;
		reader.parse(params_plain, req[Const::PARAMS], false);

		return true;
	} catch (...)
	{
		return false;
	}
}

bool RpcMethod::autoEncrypt(Value &resp, AESTools &aes)
{
	try
	{
		//没启用加密，直接成功
		if (!_isEncrypt)
		{
			return true;
		}

		//如果是notification(id为null)，直接返回
		if (resp[Const::ID] == Json::nullValue)
		{
			return true;
		}

		//如果不为null,将result加密
		Value result = resp[Const::RESULT];
		if (result == Json::nullValue)
		{
			//null的不加密
			return true;
		}
		else
		{
			//不是null，加密
			Json::FastWriter writer;
			string result_plain = writer.write(result);
			string result_cipher = aes.encryptWithBase64(result_plain);
			resp[Const::RESULT] = Json::Value(result_cipher);
			return true;
		}
	} catch (...)
	{
		return false;
	}
}

void RpcMethod::setEncrypt(bool encrypt)
{
	_isEncrypt = encrypt;
}
